View Javadoc
1   package org.apache.maven.surefire.junit;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.lang.reflect.InvocationTargetException;
23  import java.lang.reflect.Method;
24  import java.lang.reflect.Modifier;
25  import java.util.ArrayList;
26  import java.util.List;
27  import org.apache.maven.surefire.report.LegacyPojoStackTraceWriter;
28  import org.apache.maven.surefire.report.ReportEntry;
29  import org.apache.maven.surefire.report.RunListener;
30  import org.apache.maven.surefire.report.SimpleReportEntry;
31  import org.apache.maven.surefire.testset.TestSetFailedException;
32  
33  /**
34   * Executes a JUnit3 test class
35   *
36   */
37  public class PojoTestSet
38      implements SurefireTestSet
39  {
40      private static final String TEST_METHOD_PREFIX = "test";
41  
42      private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
43  
44      private final Object testObject;
45  
46      private final Class<?> testClass;
47  
48      private List<Method> testMethods;
49  
50      private Method setUpMethod;
51  
52      private Method tearDownMethod;
53  
54      public PojoTestSet( Class<?> testClass )
55          throws TestSetFailedException
56      {
57          if ( testClass == null )
58          {
59              throw new IllegalArgumentException( "testClass is null" );
60          }
61  
62          this.testClass = testClass;
63  
64          try
65          {
66              testObject = testClass.newInstance();
67          }
68          catch ( InstantiationException e )
69          {
70              throw new TestSetFailedException( "Unable to instantiate POJO '" + testClass + "'", e );
71          }
72          catch ( IllegalAccessException e )
73          {
74              throw new TestSetFailedException( "Unable to instantiate POJO '" + testClass + "'", e );
75          }
76      }
77  
78      public void execute( RunListener reportManager, ClassLoader loader )
79          throws TestSetFailedException
80      {
81          if ( reportManager == null )
82          {
83              throw new NullPointerException( "reportManager is null" );
84          }
85  
86          executeTestMethods( reportManager );
87      }
88  
89      private void executeTestMethods( RunListener reportManager )
90      {
91          if ( reportManager == null )
92          {
93              throw new NullPointerException( "reportManager is null" );
94          }
95  
96          if ( testMethods == null )
97          {
98              discoverTestMethods();
99          }
100 
101         boolean abort = false;
102 
103         for ( int i = 0; i < testMethods.size() && !abort; ++i )
104         {
105             abort = executeTestMethod( testMethods.get( i ), EMPTY_OBJECT_ARRAY, reportManager );
106         }
107     }
108 
109     private boolean executeTestMethod( Method method, Object[] args, RunListener reportManager )
110     {
111         if ( method == null || args == null || reportManager == null )
112         {
113             throw new NullPointerException();
114         }
115 
116         String userFriendlyMethodName = method.getName() + '(';
117 
118         if ( args.length != 0 )
119         {
120             userFriendlyMethodName += "Reporter";
121         }
122 
123         userFriendlyMethodName += ')';
124 
125         ReportEntry report =
126             new SimpleReportEntry( testObject.getClass().getName(), getTestName( userFriendlyMethodName ) );
127 
128         reportManager.testStarting( report );
129 
130         try
131         {
132             setUpFixture();
133         }
134         catch ( Throwable e )
135         {
136             report =
137                 SimpleReportEntry.withException( testObject.getClass().getName(), getTestName( userFriendlyMethodName ),
138                                                  new LegacyPojoStackTraceWriter( testObject.getClass().getName(),
139                                                                                  method.getName(), e ) );
140 
141             reportManager.testFailed( report );
142 
143             // A return value of true indicates to this class's executeTestMethods
144             // method that it should abort and not attempt to execute
145             // any other test methods. The other caller of this method,
146             // TestRerunner.rerun, ignores this return value, because it is
147             // only running one test.
148             return true;
149         }
150 
151         // Make sure that tearDownFixture
152         try
153         {
154             method.invoke( testObject, args );
155 
156             report = new SimpleReportEntry( testObject.getClass().getName(), getTestName( userFriendlyMethodName ) );
157 
158             reportManager.testSucceeded( report );
159         }
160         catch ( InvocationTargetException ite )
161         {
162             Throwable t = ite.getTargetException();
163 
164             report =
165                 SimpleReportEntry.withException( testObject.getClass().getName(), getTestName( userFriendlyMethodName ),
166                                                  new LegacyPojoStackTraceWriter( testObject.getClass().getName(),
167                                                                                  method.getName(), t ) );
168 
169             reportManager.testFailed( report );
170             // Don't return  here, because tearDownFixture should be called even
171             // if the test method throws an exception.
172         }
173         catch ( Throwable t )
174         {
175             report =
176                 SimpleReportEntry.withException( testObject.getClass().getName(), getTestName( userFriendlyMethodName ),
177                                                  new LegacyPojoStackTraceWriter( testObject.getClass().getName(),
178                                                                                  method.getName(), t ) );
179 
180             reportManager.testFailed( report );
181             // Don't return  here, because tearDownFixture should be called even
182             // if the test method throws an exception.
183         }
184 
185         try
186         {
187             tearDownFixture();
188         }
189         catch ( Throwable t )
190         {
191             // Treat any exception from tearDownFixture as a failure of the test.
192             report =
193                 SimpleReportEntry.withException( testObject.getClass().getName(), getTestName( userFriendlyMethodName ),
194                                                  new LegacyPojoStackTraceWriter( testObject.getClass().getName(),
195                                                                                  method.getName(), t ) );
196 
197             reportManager.testFailed( report );
198 
199             // A return value of true indicates to this class's executeTestMethods
200             // method that it should abort and not attempt to execute
201             // any other test methods. The other caller of this method,
202             // TestRerunner.rerun, ignores this return value, because it is
203             // only running one test.
204             return true;
205         }
206 
207         // A return value of false indicates to this class's executeTestMethods
208         // method that it should keep plowing ahead and invoke more test methods.
209         // The other caller of this method,
210         // TestRerunner.rerun, ignores this return value, because it is
211         // only running one test.
212         return false;
213     }
214 
215     private String getTestName( String testMethodName )
216     {
217         if ( testMethodName == null )
218         {
219             throw new NullPointerException( "testMethodName is null" );
220         }
221 
222         return getTestClass().getName() + "." + testMethodName;
223     }
224 
225     private void setUpFixture()
226         throws Throwable
227     {
228         if ( setUpMethod != null )
229         {
230             setUpMethod.invoke( testObject );
231         }
232     }
233 
234     private void tearDownFixture()
235         throws Throwable
236     {
237         if ( tearDownMethod != null )
238         {
239             tearDownMethod.invoke( testObject );
240         }
241     }
242 
243     private void discoverTestMethods()
244     {
245         if ( testMethods == null )
246         {
247             testMethods = new ArrayList<Method>();
248 
249             Method[] methods = getTestClass().getMethods();
250 
251             for ( Method m : methods )
252             {
253                 if ( isValidTestMethod( m ) )
254                 {
255                     String simpleName = m.getName();
256 
257                     // name must have 5 or more chars
258                     if ( simpleName.length() > 4 )
259                     {
260                         String firstFour = simpleName.substring( 0, 4 );
261 
262                         // name must start with "test"
263                         if ( firstFour.equals( TEST_METHOD_PREFIX ) )
264                         {
265                             testMethods.add( m );
266                         }
267                     }
268                 }
269                 else if ( m.getName().equals( "setUp" ) && m.getParameterTypes().length == 0 )
270                 {
271                     setUpMethod = m;
272                 }
273                 else if ( m.getName().equals( "tearDown" ) && m.getParameterTypes().length == 0 )
274                 {
275                     tearDownMethod = m;
276                 }
277             }
278         }
279     }
280 
281     private static boolean isValidTestMethod( Method m )
282     {
283         boolean isInstanceMethod = !Modifier.isStatic( m.getModifiers() );
284 
285         boolean returnsVoid = m.getReturnType().equals( void.class );
286 
287         boolean hasNoParams = m.getParameterTypes().length == 0;
288 
289         return isInstanceMethod && returnsVoid && hasNoParams;
290     }
291 
292     public String getName()
293     {
294         return testClass.getName();
295     }
296 
297     public Class<?> getTestClass()
298     {
299         return testClass;
300     }
301 }